package beans;

import factory.BeanDefinitionRegisty;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import resource.ResourceLoader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;

/**
 * Created by inuyasha on 18-3-22.
 */
public class XmlBeanDefinitionReader implements BeanDefinitionReader {

    private BeanDefinitionRegisty registy;

    private ResourceLoader loader;

    public XmlBeanDefinitionReader(BeanDefinitionRegisty registry, ResourceLoader resourceLoader) {
        this.registy = registry;
        this.loader = resourceLoader;
    }

    public void loadBeanDefinition(String location) throws Exception {
        InputStream is = this.getLoader().getResource(location).getInputStream();
        doLoadBeanDefinition(is);
    }

    public ResourceLoader getLoader() {
        return this.loader;
    }

    public BeanDefinitionRegisty getRegisty() {
        return this.registy;
    }

    protected void doLoadBeanDefinition(InputStream is) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(is);
        registerBeanDefinitions(document);
        is.close();
    }


    protected void registerBeanDefinitions(Document document) {
        Element root = document.getDocumentElement();
        NodeList nodeList = root.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node item = nodeList.item(i);
            if (item instanceof Element) {
                Element ele = (Element) item;
                processBeanDefinition(ele);
            }
        }
    }


    protected void processBeanDefinition(Element ele) {
        String name = ele.getAttribute("id");
        String className = ele.getAttribute("class");
        if (className == null || className.length() == 0) {
            throw new IllegalArgumentException("Configuration exception: <bean> element must has class attribute.");
        }
        if (name == null || name.length() == 0) {
            name = className;
        }
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setClassName(className);
//        beanDefinition.setaClass(Class.forName(className));
        beanDefinition.setName(name);
        processBeanProperty(ele, beanDefinition);
        getRegisty().registerBeanDefinition(name, beanDefinition);
    }

    protected void processBeanProperty(Element ele, BeanDefinition beanDefinition) {
        NodeList childs = ele.getElementsByTagName("property");
        for (int i = 0; i < childs.getLength(); i++) {
            Node node = childs.item(i);
            if (node instanceof Element) {
                Element property = (Element) node;
                String name = property.getAttribute("name");
                String value = property.getAttribute("value");
                if (value != null && value.length() > 0) {
                    beanDefinition.getPropertyValues().addList(new PropertyValue(name, value));
                } else {
                    String ref = property.getAttribute("ref");
                    if (ref == null || ref.length() == 0) {
                        throw new IllegalArgumentException("Configuration problem: <property> element for " +
                                name + " must specify a value or ref.");
                    }
                    BeanReference reference = new BeanReference(ref);
                    beanDefinition.getPropertyValues().addList(new PropertyValue(name, reference));
                }
            }
        }
    }
}
